# proevolytx@gmail.com
# 2012/12/5

def Translate(root, out, env, settings):
    if type(root) == list:
        for node in root:
            numPush = Translate(node, out, env, settings)
            for i in range(numPush):
                out.append(["pop"])
        return 0

    elif root["type"] in ["int", "float", "string"]:
        out.append(["create", root["type"], root["content"]])
        return 1

    elif root["type"] == "id":
        out.append(["push", root["content"]])
        return 1

    elif root["type"] == "binary-op":
        return TranslateBinaryOp(root, out, env, settings)

    elif root["type"] == "mono-op":
        return TranslateMonoOp(root, out, env, settings)

    elif root["type"] == "call":
        return TranslateCall(root, out, env, settings)

    elif root["type"] == "print":
        return TranslatePrint(root, out, env, settings)

    elif root["type"] == "assign":
        return TranslateAssign(root, out, env, settings)

    elif root["type"] == "if":
        return TranslateIf(root, out, env, settings)

    elif root["type"] == "while":
        return TranslateWhile(root, out, env, settings)

    elif root["type"] == "break":
        if len(settings["loop"]) == 0:
            raise Exception("Break met outside loop body.")
        # must pop the exception stack of break out of the try-body
        for i in range(settings["ex"] - settings["loop"][-1][2]):
            out.append(["popex"])
        out.append(["goto", settings["loop"][-1][1]])
        return 0

    elif root["type"] == "continue":
        if len(settings["loop"]) == 0:
            raise Exception("Continue met outside loop body.")
        # must pop the exception stack of break out of the try-body
        for i in range(settings["ex"] - settings["loop"][-1][2]):
            out.append(["popex"])
        out.append(["goto", settings["loop"][-1][0]])
        return 0

    elif root["type"] == "function":
        return TranslateFunction(root, out, env, settings, "func")

    elif root["type"] == "return":
        return TranslateReturn(root, out, env, settings)

    elif root["type"] == "list" or root["type"] == "tuple":
        return TranslateListTuple(root, out, env, settings)

    elif root["type"] == "index":
        return TranslateIndex(root, out, env, settings)

    elif root["type"] == "slice":
        return TranslateSlice(root, out, env, settings)

    elif root["type"] == "del":
        return TranslateDel(root, out, env, settings)

    elif root["type"] == "raise":
        return TranslateRaise(root, out, env, settings)

    elif root["type"] == "assert":
        return TranslateAssert(root, out, env, settings)

    elif root["type"] == "try":
        return TranslateTry(root, out, env, settings)

    elif root["type"] == "for":
        return TranslateFor(root, out, env, settings)

    elif root["type"] == "dict":
        return TranslateDict(root, out, env, settings)

    elif root["type"] == "class":
        return TranslateClass(root, out, env, settings)

    elif root["type"] == "import":
        return TranslateImport(root, out, env, settings)

    elif root["type"] == "exec":
        return TranslateExec(root, out, env, settings)

    elif root["type"] == "pass":
        return 0
    
    else:
        raise Exception("Node type '" + root["type"] + "' not supported.")

# create an unused temp variable
def NewTempVar(settings):
    result = "__tmp" + str(settings["var"])
    settings["var"] += 1
    return result

# create an unused lable
def NewLabel(settings):
    result = settings["label"]
    settings["label"] += 1
    return result

BinaryOpToName = {
    "+" : "ADD",
    "-" : "SUB",
    "*" : "MUL",
    "/" : "DIV",
    "<" : "LT",
    "<=" : "LTEQ",
    ">" : "GT",
    ">=" : "GTEQ",
    "!=" : "NEQ",
    "==" : "EQ",
    "%" : "MOD",
    "in" : "IN",
    "notin" : "NOTIN",
    "is" : "IS",
    "isnot" : "ISNOT"}

# binary-op: left op right
def TranslateBinaryOp(root, out, env, settings):
    assert root["type"] == "binary-op"

    op = root["operator"]["content"]
    left = root["left"]
    right = root["right"]

    # we need to calc LEFT first
    # LEFT 1
    # tmpvar = LEFT 0
    # RIGHT 1
    # tmpvar 2
    # subid op 2
    # call 1
    if op in BinaryOpToName:
        Translate(left, out, env, settings)
        tmpvar = NewTempVar(settings)
        out.append(["newid", tmpvar])
        out.append(["push", tmpvar])
        out.append(["assign"])
        out.append(["pop"])
        out.append(["pop"])
        Translate(right, out, env, settings)
        out.append(["create", "int", 1])
        out.append(["push", tmpvar])
        out.append(["delid", tmpvar])
        
        out.append(["subid", BinaryOpToName[op]])
        out.append(["call"])

    # only met in non-assign statements
    # LEFT 1
    # subid RIGHT 1
    elif op == ".":
        Translate(left, out, env, settings)
        assert right["type"] == "id"
        out.append(["subid", right["content"]])

    # specially for 'or' to support shortcut
    # LEFT 1
    # LEFT 2
    # ifnot rightlbl 1 (need to test the second expr)
    # goto shortcut 1 (shortcut)
    # label rightlbl 1 (test the second expr)
    # pop 0
    # RIGHT 1
    # label shortcut 1
    # create int 1 2 (convert to bool type)
    # push bool 3
    # call 1
    elif op == "or":
        Translate(left, out, env, settings)
        shortcut = NewLabel(settings)
        rightlbl = NewLabel(settings)
        out.append(["copytop", 0])
        out.append(["ifnot", rightlbl])
        out.append(["goto", shortcut])
        out.append(["label", rightlbl])
        out.append(["pop"])
        Translate(right, out, env, settings)
        out.append(["label", shortcut])
        out.append(["create", "int", 1])
        out.append(["push", "bool"])
        out.append(["call"])

    # specially for 'and' to support shortcut
    # LEFT 1
    # LEFT 2
    # ifnot shortcut 1 (shortcut)
    # pop 0 (test the second expr)
    # RIGHT 1
    # label shortcut 1
    # create int 1 2 (convert to bool type)
    # push bool 3
    # call 1
    elif op == "and":
        Translate(left, out, env, settings)
        shortcut = NewLabel(settings)
        out.append(["copytop", 0])
        out.append(["ifnot", shortcut])
        out.append(["pop"])
        Translate(right, out, env, settings)
        out.append(["label", shortcut])
        out.append(["create", "int", 1])
        out.append(["push", "bool"])
        out.append(["call"])

    else:
        raise Exception("Operator type not supported.")

    return 1

MonoOpToName = {
    "+" : "POS",
    "-" : "NEG",
    "not" : "NOT"}

# mono-op: op right
# create int 0
# RIGHT 1
# subid op 1
# call
def TranslateMonoOp(root, out, env, settings):
    assert root["type"] == "mono-op"

    op = root["operator"]["content"]
    assert op in MonoOpToName
    right = root["right"]
    
    out.append(["create", "int", 0])
    Translate(right, out, env, settings)
    out.append(["subid", MonoOpToName[op]])
    out.append(["call"])

    return 1

# call: args, name
# expand args in 1..n way to keep order
# but push them to stack in n..1 way to make callees easy
def TranslateCall(root, out, env, settings):
    assert root["type"] == "call"
    
    tmpvars = []
    for i in range(len(root["args"])):
        Translate(root["args"][i], out, env, settings)
        tmpvar = NewTempVar(settings)
        tmpvars.append(tmpvar)
        out.append(["newid", tmpvar])
        out.append(["push", tmpvar])
        out.append(["assign"])
        out.append(["pop"])
        out.append(["pop"])

    tmpvars.reverse()
    for tmpvar in tmpvars:
        out.append(["push", tmpvar])
        out.append(["delid", tmpvar])
    out.append(["create", "int", len(root["args"])])
    Translate(root["left"], out, env, settings)
    out.append(["call"])
    
    return 1

# print: exprs
# for statement "print", wo convert it to "print ''" first
# for each expr, we print it IMMEDIATELY after calculating it
# thus, we generate N print calls for N expressions.
def TranslatePrint(root, out, env, settings):
    assert root["type"] == "print"

    if len(root["exprs"]) == 0:
        root["exprs"].append({"type":"string", "content":""})

    for i, expr in enumerate(root["exprs"]):
        Translate(expr, out, env, settings)
        if i < len(root["exprs"]) - 1:
            out.append(["create", "int", 0])
        else:
            out.append(["create", "int", int(root["newline"])])
        out.append(["create", "int", 2])
        out.append(["push", "print"])
        out.append(["call"])
        out.append(["pop"])
    
    return 0

# the value of expression has already been pushed into the stack
def TranslateAssignSingle(root, out, env, settings):
    # a = val
    # newid a, push a, and assign
    if root["type"] == "id":
        out.append(["newid", root["content"]])
        out.append(["push", root["content"]])
        out.append(["assign"])
        out.append(["pop"])
        out.append(["pop"])

    # left[index] = val
    # by calling left.__setitem__(index, val)
    # so we push index first, then push left and subid __setitem__
    # but dont forget to calc left first
    elif root["type"] == "index":
        Translate(root["left"], out, env, settings)
        left = NewTempVar(settings)
        out.append(["newid", left])
        out.append(["push", left])
        out.append(["assign"])
        out.append(["pop"])
        out.append(["pop"])

        Translate(root["index"], out, env, settings)
        out.append(["create", "int", 2])
        out.append(["push", left])
        out.append(["subid", "__setitem__"])
        out.append(["call"])
        out.append(["pop"])
        out.append(["delid", left])

    # left[beg:end] = val
    # by calling left.__setslice__(beg, end, val)
    # so we push end, then beg, then left and subid __setslice__
    # but dont forget to calc left and beg first
    elif root["type"] == "slice":
        Translate(root["left"], out, env, settings)
        left = NewTempVar(settings)
        out.append(["newid", left])
        out.append(["push", left])
        out.append(["assign"])
        out.append(["pop"])
        out.append(["pop"])

        if root["beg"]:
            Translate(root["beg"], out, env, settings)
        else:
            out.append(["create", "none"])
        beg = NewTempVar(settings)
        out.append(["newid", beg])
        out.append(["push", beg])
        out.append(["assign"])
        out.append(["pop"])
        out.append(["pop"])

        if root["end"]:
            Translate(root["end"], out, env, settings)
        else:
            out.append(["create", "none"])
        out.append(["push", beg])
        out.append(["create", "int", 3])
        out.append(["push", left])
        out.append(["subid", "__setslice__"])
        out.append(["call"])
        out.append(["pop"])
        out.append(["delid", beg])
        out.append(["delid", left])

    # left.right = val
    # we push newsubid before subid
    elif root["type"] == "binary-op" and \
         root["operator"]["content"] == ".":
        Translate(root["left"], out, env, settings)
        out.append(["newsubid", root["right"]["content"]])
        out.append(["subid", root["right"]["content"]])
        out.append(["assign"])
        out.append(["pop"])
        out.append(["pop"])

    # we only support id = v, left[index] = v, left[beg:end] = v, left.id = v
    else:
        raise Exception("Cannot assign to elements " +
                        "except id, index and slice.")
    return 0

# assign: left:[tuple[expr]], right:tuple[expr]
def TranslateAssign(root, out, env, settings):
    assert root["type"] == "assign"

    rroot = root["right"]
    assert rroot["type"] == "tuple"

    ids = []
    # calculate the right part first
    for expr in rroot["tuple"]:
        Translate(expr, out, env, settings)
        tmpid = NewTempVar(settings)
        ids.append(tmpid)
        out.append(["newid", tmpid])
        out.append(["push", tmpid])
        out.append(["assign"])
        out.append(["pop"])
        out.append(["pop"])

    rlen = len(ids)
    # assign the right part to the left part
    # for a = b = c, we do a = c first, then b = c
    # note that c will not be recalculated
    for lroot in root["left"]:
        llen = len(lroot["tuple"])

        # a1, a2, ..., an = b1, b2, ..., bn
        # we do a1 = b1, a2 = b2, ..., an = bn
        if llen == rlen:
            for i in range(llen):
                out.append(["push", ids[i]])
                TranslateAssignSingle(lroot["tuple"][i], out, env, settings)

        # a = b1, b2, ..., bn
        # we create a tuple (b1, b2, ..., bn)
        # then assign it to a
        elif llen == 1:
            out.append(["create", "tuple"])
            for exprid in ids:
                out.append(["push", exprid])
                out.append(["create", "int", 1])
                out.append(["copytop", 2])
                out.append(["subid", "INIT_APPEND"])
                out.append(["call"])
                out.append(["pop"])
            TranslateAssignSingle(lroot["tuple"][0], out, env, settings)

        # a1, a2, ..., an = b
        # b must be tuple or list with length > n
        # we call a1 = b[0], a2 = b[1], ..., an = b[n - 1]
        elif rlen == 1:
            for i in range(llen):
                out.append(["create", "int", i])
                out.append(["create", "int", 1])
                out.append(["push", ids[0]])
                out.append(["subid", "__getitem__"])
                out.append(["call"])
                TranslateAssignSingle(lroot["tuple"][i], out, env, settings)
                
        else:
            raise Exception("Invalid assignment.")

    for idvar in ids:
        out.append(["delid", idvar])
    
    return 0

# if: chain[[expr, body]], else?
# for each pair in chain
#   label i
#   EXPR-i
#   ifnot label i+1
#   BODY-i
#   goto if-end
# label i+1
# ELSE (empty if else is None)
# label if-end
def TranslateIf(root, out, env, settings):
    assert root["type"] == "if"

    labels = []
    for i in range(len(root["chain"]) + 2):
        labels.append(NewLabel(settings))

    for i, c in enumerate(root["chain"]):
        expr = c[0]
        block = c[1]
        out.append(["label", labels[i]])
        Translate(expr, out, env ,settings)
        out.append(["ifnot", labels[i + 1]])
        Translate(block, out, env, settings)
        out.append(["goto", labels[-1]])

    out.append(["label", labels[-2]])
    if root["else"] != None:
        Translate(root["else"], out, env, settings)
    out.append(["label", labels[-1]])

    return 0

# while: expr, body
# label LOOP
# EXPR
# ifnot label END
# BODY
# goto LOOP
# label END
# note that [LOOP, END, EX] is push to settings loop stack
# in order to translate break(goto END), continue(goto LOOP) in BODY
# see above in Translate
def TranslateWhile(root, out, env, settings):
    assert root["type"] == "while"

    label1 = NewLabel(settings)
    label2 = NewLabel(settings)

    out.append(["label", label1])
    Translate(root["expr"], out, env, settings)
    out.append(["ifnot", label2])
    settings["loop"].append([label1, label2, settings["ex"]])
    Translate(root["body"], out, env, settings)
    del settings["loop"][-1]
    out.append(["goto", label1])
    out.append(["label", label2])

    return 0

# func: name args[[id, default]]
def TranslateFunction(root, out, env, settings, functype):
    assert root["type"] == "function"

    env[-1].add(root["name"]["content"])
    env.append(set())

    label1 = NewLabel(settings)
    label2 = NewLabel(settings)

    # we create a function object with its start label
    # then assign it to NAME
    out.append(["create", functype, label1])
    out.append(["newid", root["name"]["content"]])
    out.append(["push", root["name"]["content"]])
    out.append(["assign"])
    out.append(["pop"])
    out.append(["pop"])
    out.append(["goto", label2]) # dont run the function when creating it

    # the entry label of function
    out.append(["label", label1])

    # get argument count given by the caller
    argc = NewTempVar(settings)
    out.append(["newid", argc])
    out.append(["push", argc])
    out.append(["assign"])
    out.append(["pop"])
    out.append(["pop"])

    labels = []
    for i in range(len(root["args"])):
        labels.append(NewLabel(settings))

    for i, arg in enumerate(root["args"]):
        # check if the argument is enough
        out.append(["push", argc])
        out.append(["create", "int", 1])
        out.append(["create", "int", 0])
        out.append(["subid", "EQ"])
        out.append(["call"])
        out.append(["ifnot", labels[i]])

        # if not enough, use the default value
        if arg[1]:
            Translate(arg[1], out, env, settings)

        # if enough, use the argument given by the caller
        out.append(["label", labels[i]])
        out.append(["newid", arg[0]["content"]])
        env[-1].add(arg[0]["content"])
        out.append(["push", arg[0]["content"]])
        out.append(["assign"])
        out.append(["pop"])
        out.append(["pop"])

        # decrease the argument count
        out.append(["push", argc])
        out.append(["create", "int", 1])
        out.append(["create", "int", -1])
        out.append(["subid", "ADD"])
        out.append(["call"])
        out.append(["push", argc])
        out.append(["assign"])
        out.append(["pop"])
        out.append(["pop"])
        
    out.append(["delid", argc])
    Translate(root["body"], out, env, settings)

    # if the function is the __init__ method
    # then we should return the current instance instead of none
    if functype == "method" and root["name"]["content"] == "__init__":
        out.append(["push", root["args"][0][0]["content"]])
    # we add return None at the bottom of each function
    else:
        out.append(["create", "none"])
    out.append(["ret"])

    out.append(["label", label2])

    del env[-1]
    return 0

def TranslateReturn(root, out, env, settings):
    if root["expr"]:
        Translate(root["expr"], out, env, settings)
    else:
        out.append(["create", "none"])
    out.append(["ret"])
    return 0

# we build an empty list/tuple first
# then append the initial items to it
# we use inner function INIT_APPEND instead of append
#because append is not supported by tuple
def TranslateListTuple(root, out, env, settings):
    assert root["type"] in ["list", "tuple"]

    out.append(["create", root["type"]])

    for expr in root[root["type"]]:
        Translate(expr, out, env, settings)
        out.append(["create", "int", 1])
        out.append(["copytop", 2])
        out.append(["subid", "INIT_APPEND"])
        out.append(["call"])
        out.append(["pop"])
        
    return 1

# we build an empty dict first
# then used __setitem__ to initialize it
def TranslateDict(root, out, env, settings):
    assert root["type"] == "dict"

    out.append(["create", "dict"])

    for pair in root["dict"]:
        Translate(pair[1], out, env, settings)
        Translate(pair[0], out, env, settings)
        out.append(["create", "int", 2])
        out.append(["copytop", 3])
        out.append(["subid", "__setitem__"])
        out.append(["call"])
        out.append(["pop"])
    
    return 1

# similar to binary-op
def TranslateIndex(root, out, env, settings):
    assert root["type"] == "index"

    tmpvar = NewTempVar(settings)
    Translate(root["left"], out, env, settings)
    out.append(["newid", tmpvar])
    out.append(["push", tmpvar])
    out.append(["assign"])
    out.append(["pop"])
    out.append(["pop"])
    
    Translate(root["index"], out, env, settings)
    out.append(["create", "int", 1])
    out.append(["push", tmpvar])
    out.append(["subid", "INDEX"])
    out.append(["call"])
    out.append(["delid", tmpvar])
    
    return 1

# similar to index
def TranslateSlice(root, out, env, settings):
    assert root["type"] == "slice"

    tmpleft = NewTempVar(settings)
    tmpbeg = NewTempVar(settings)

    Translate(root["left"], out, env, settings)
    out.append(["newid", tmpleft])
    out.append(["push", tmpleft])
    out.append(["assign"])
    out.append(["pop"])
    out.append(["pop"])

    if root["beg"]:
        Translate(root["beg"], out, env, settings)
    else:
        out.append(["create", "none"])
    out.append(["newid", tmpbeg])
    out.append(["push", tmpbeg])
    out.append(["assign"])
    out.append(["pop"])
    out.append(["pop"])
    
    if root["end"]:
        Translate(root["end"], out, env, settings)
    else:
        out.append(["create", "none"])
    out.append(["push", tmpbeg])
    out.append(["create", "int", 2])
    out.append(["push", tmpleft])
    out.append(["subid", "SLICE"])
    out.append(["call"])
    out.append(["delid", tmpbeg])
    out.append(["delid", tmpleft])
    
    return 1

# del: [expr]
# similar to assign
def TranslateDel(root, out, env, settings):
    assert root["type"] == "del"

    for expr in root["exprs"]:
        # del id
        if expr["type"] == "id":
            out.append(["delid", expr["content"]])

        # del left[index]
        # we call left.__delitem__[index]
        elif expr["type"] == "index":
            tmpvar = NewTempVar(settings)
            Translate(expr["left"], out, env, settings)
            out.append(["newid", tmpvar])
            out.append(["push", tmpvar])
            out.append(["assign"])
            out.append(["pop"])
            out.append(["pop"])
            
            Translate(expr["index"], out, env, settings)
            out.append(["create", "int", 1])
            out.append(["push", tmpvar])
            out.append(["subid", "DELINDEX"])
            out.append(["call"])
            out.append(["pop"])
            out.append(["delid", tmpvar])

        # del left[beg:end]
        # we call left.__delslice__[beg:end]
        elif expr["type"] == "slice":
            tmpleft = NewTempVar(settings)
            tmpbeg = NewTempVar(settings)
            
            Translate(expr["left"], out, env, settings)
            out.append(["newid", tmpleft])
            out.append(["push", tmpleft])
            out.append(["assign"])
            out.append(["pop"])
            out.append(["pop"])

            if expr["beg"]:
                Translate(expr["beg"], out, env, settings)
            else:
                out.append(["create", "none"])
            out.append(["newid", tmpbeg])
            out.append(["push", tmpbeg])
            out.append(["assign"])
            out.append(["pop"])
            out.append(["pop"])
            
            if expr["end"]:
                Translate(expr["end"], out, env, settings)
            else:
                out.append(["create", "none"])
            out.append(["push", tmpbeg])
            out.append(["create", "int", 2])
            out.append(["push", tmpleft])
            out.append(["subid", "DELSLICE"])
            out.append(["call"])
            out.append(["pop"])
            out.append(["delid", tmpleft])
            out.append(["delid", tmpbeg])

        else:
            raise Exception("Del only support id, index and slice.")
    
    return 0

def TranslateRaise(root, out, env, settings):
    assert root["type"] == "raise"
    Translate(root["expr"], out, env, settings)
    out.append(["raise"])
    return 0

# we raise Assertion failed Exception when assertion failed
def TranslateAssert(root, out, env, settings):
    assert root["type"] == "assert"
    out.append(["create", "int", 0])
    Translate(root["expr"], out, env, settings)
    out.append(["subid", "NOT"])
    out.append(["call"])
    lbl = NewLabel(settings)
    out.append(["ifnot", lbl])
    out.append(["create", "string", "Assertion failed."])
    out.append(["raise"])
    out.append(["label", lbl])
    return 0

# we push the exception stack
# in case to detect break, continue out of try body
def TranslateTry(root, out, env, settings):
    assert root["type"] == "try"
    
    good = NewLabel(settings)
    bad = NewLabel(settings)
    
    out.append(["pushex", bad])
    settings["ex"] += 1
    Translate(root["try-body"], out, env, settings)
    settings["ex"] -= 1
    out.append(["popex"])
    out.append(["goto", good])

    out.append(["label", bad])
    out.append(["newid", root["except-id"]["content"]])
    out.append(["push", root["except-id"]["content"]])
    out.append(["assign"])
    out.append(["pop"])
    out.append(["pop"])
    Translate(root["except-body"], out, env, settings)
    out.append(["label", good])

    return 0

# for a1, a2, ..., an in b:
#   body
#
# will be converted to
#
# __it = b.__iter__()
# while True:
#   try:
#     a1, a2, ..., an = __it.next() (StopIteration Exception)
#   except Exception, e:
#     break
#   body
#
# first, then translated to intermediate code
def TranslateFor(root, out, env, settings):
    assert root["type"] == "for"

    loop = NewLabel(settings)
    finish = NewLabel(settings)

    # calc b
    out.append(["create", "int", 0])
    expr = {"type" : "call",
            "left" : {"type" : "id", "content" : "list"},
            "args" : [root["expr"]]}
    Translate(expr, out, env, settings)
    tmp = NewTempVar(settings)
    out.append(["newid", tmp])
    out.append(["push", tmp])
    out.append(["assign"])
    out.append(["pop"])
    out.append(["pop"])
    out.append(["push", tmp])

    # calc b.__iter__
    out.append(["subid", "__iter__"])
    out.append(["call"])

    # assign to __it
    it = NewTempVar(settings)
    out.append(["newid", it])
    out.append(["push", it])
    out.append(["assign"])
    out.append(["pop"])
    out.append(["pop"])
    out.append(["label", loop])

    # build syntax tree: a1, a2, ..., an = __it.next()
    ltup = {"type":"tuple", "tuple":root["vals"]}
    getnext = {
        "type" : "binary-op",
        "left" : {"type":"id", "content":it},
        "right" : {"type":"id", "content":"next"},
        "operator" : {"type":"keyword", "content":"."}}
    nextop = {"type":"call", "args":[], "left":getnext}
    rtup = {"type":"tuple", "tuple":[nextop]}
    assign = {"type":"assign", "right":rtup, "left":[ltup]}
    # build syntax tree: try: a1, a2, ..., an = __it.next() except: break
    tryex = {
        "type" : "try",
        "except-id" : {"type":"id", "content":"__exceptid"},
        "except-body" : [{"type":"break"}],
        "try-body" : [assign]}
    #SimplePrint(tryex)

    settings["loop"].append([loop, finish, settings["ex"]])
    Translate(tryex, out, env, settings)
    Translate(root["body"], out, env, settings)
    del settings["loop"][-1]

    out.append(["goto", loop])
    out.append(["label", finish])
    out.append(["delid", it])

    return 0

# class: methods[[func, isstatic]] body parent
def TranslateClass(root, out, env, settings):
    assert root["type"] == "class"

    # create a class object
    out.append(["create", "class", root["parent"]])
    out.append(["newid", root["name"]["content"]])
    out.append(["push", root["name"]["content"]])
    out.append(["assign"])
    out.append(["pop"])
    out.append(["pop"])

    # when inclass is executed, we go into the namespace of the class
    # so inclass A, x = 123, outclass is equal to A.x = 123
    out.append(["inclass", root["name"]["content"]])

    for pair in root["methods"]:
        method = pair[0]
        isstatic = pair[1]
        if isstatic:
            # static function, with @staticmethod tag
            TranslateFunction(method, out, env, settings, "func")
        else:
            TranslateFunction(method, out, env, settings, "method")

    # class body
    Translate(root["body"], out, env, settings)
    out.append(["outclass"])
    
    return 0

# import: libname func expand
def TranslateImport(root, out, env, settings):
    assert root["type"] == "import"
    if "expand" in root:
        # may already expanded by parser
        # translate the expanded tree
        return Translate(root["expand"], out, env, settings)
    else:
        # or translate to import instruction
        # these libs are not given by user
        # so we will ask python-runtime when needed
        if root["func"] is None:
            func = None
        else:
            func = root["func"]["content"]
        out.append(["import", root["lib"]["content"], func])
        return 0

# exec: expr
def TranslateExec(root, out, env, settings):
    assert root["type"] == "exec"
    Translate(root["expr"], out, env, settings)
    out.append(["exec"])
    return 0

from lexer import *
from preparser import *
from parser_topdown import *
from time import *
from machine_hi import *

if __name__ == "__main__":
    streampath = "test/test5.py"
    fs = FileStream(streampath)
    fpath, fname = path.split(streampath)

    start = clock()
    print "============================================"
    print "Building..."
    print "============================================"
    lex = Lexer()
    lexerBuildTime = clock() - start

    parser = BuildParser(4)

    parseResults = Parse(fs, lex, parser, fpath)

    start = clock()
    hi = []
    settings = {"label" : 0, "var" : 0, "loop" : [], "ex" : 0}
    tree = parseResults["__main__"]["parser-out"]
    Translate(tree, hi, [set()], settings)
    hi.append(["halt"])
    translateTime = clock() - start

    tokenCount = 0
    nodeCount = 0
    lineCount = 0
    totalLexerTime = 0
    totalParserTime = 0
    for r in parseResults:
        tokenCount += parseResults[r]["lexer-tokens"]
        nodeCount += parseResults[r]["parser-nodes"]
        lineCount += parseResults[r]["lexer-lines"]
        totalLexerTime += parseResults[r]["lexer-time"]
        totalParserTime += parseResults[r]["parser-time"]
        print "Module:", r
        print "Lines of code:", parseResults[r]["lexer-lines"]
        print "Lexer time:", \
              str(parseResults[r]["lexer-time"])[:5], "seconds"
        print "Lexer token count:", parseResults[r]["lexer-tokens"]
        print "Parser time:", \
              str(parseResults[r]["parser-time"])[:5], "seconds"
        print "Parser tree size:", parseResults[r]["parser-nodes"]
        print

    print "Time Statistics"
    print "Lexer build time :", str(lexerBuildTime)[:5], "seconds"
    print "Total lexer time: ", str(totalLexerTime)[:5], "seconds"
    print "Total parser time: ",  str(totalParserTime)[:5], "seconds"
    print "Total translator time: ", str(translateTime)[:5], "seconds"
    print 

    print "Total Statistics"
    print "File count : ", len(parseResults)
    print "Total lines of code : ", lineCount
    print "Total token count : ", tokenCount
    print "Total syntax tree size : ", nodeCount
    print "Total instruction count : ", len(hi)
    
    print "============================================"
    print "Loading Instruction..."
    mac = MachineHi()
    mac.Load(hi)
    print "Simulating..."
    print "============================================"
    start = clock()
    mac.Simulate(0)
    print "============================================"
    print "Simulation finished"
    print "Instruction executed :", mac.instcount
    print "Time consumed :", str(clock() - start)[:5], "seconds"
    print "============================================"
